package org.baojie.raft.message;

import org.baojie.raft.delay.AbstractValue;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class RaftMsgSignal extends AbstractValue {

    private final int waiters;
    private final int oldStamp;
    private final String msgId;
    private final AtomicInteger waitCounts;
    private final VoteResponse[] entitys;
    private final Semaphore signals;
    private final AtomicBoolean cancel = new AtomicBoolean(false);

    public RaftMsgSignal(String msgId, int waitCounts, int oldStamp) {
        this.msgId = msgId;
        this.oldStamp = oldStamp;
        this.waiters = waitCounts;
        this.waitCounts = new AtomicInteger(waitCounts);
        this.entitys = new VoteResponse[waitCounts];
        this.signals = new Semaphore(waitCounts);
        prepare();
    }

    private void prepare() {
        try {
            signals.acquire(entitys.length);
        } catch (InterruptedException e) {
            throw new RuntimeException();
        }
    }

    @Override
    public Type type() {
        return Type.WaitVoteMsg;
    }

    public boolean acquireOne(long wait, TimeUnit unit) {
        try {
            return signals.tryAcquire(1, wait, unit);
        } catch (InterruptedException e) {
            return false;
        }
    }

    public int release(VoteResponse response) {
        int index = waitCounts.decrementAndGet();
        if (index < 0) {
            throw new IllegalArgumentException();
        } else {
            entitys[index] = response;
            releaseOne();
            return index;
        }
    }

    public String getMsgId() {
        return msgId;
    }

    public void releaseAll() {
        int all = entitys.length;
        for (int i = 0; i < all; i++) {
            releaseOne();
        }
    }

    private void releaseOne() {
        signals.release(1);
    }

    public VoteResponse getEntity(int index) {
        return entitys[index];
    }

    public int waitHolder() {
        return waiters;
    }

    public boolean setCancel() {
        if (cancel.get()) {
            return false;
        } else {
            return cancel.compareAndSet(false, true);
        }
    }

    public boolean isCanceled() {
        return cancel.get();
    }

    public int getOldStamp() {
        return oldStamp;
    }
}
